#include <iostream>
#include <array>
#include <tuple>
#include <string>
#include <sstream>
#include <memory>

using namespace std;
/*
std::array是在C++11中才引入的，与内置数组相比，array是一种更安全、更容易使用的数组类型。与内置数组类似，array对象的大小是固定的。

因此，array不支持添加和删除元素以及改变容器大小的操作。与内置数组一样，标准库array的大小也是类型的一部分。

当定义一个array时，除了指定元素类型，还要指定容器大小。为了使用array类型，我们必须同时指定元素类型和大小。

array仅仅是为普通数组添加了一些成员或全局函数，这使得数组能够被当成标准容器来使用。array不能被动态地扩展或压缩。

const并不能代表“常量”，它仅仅是对变量的一个修饰，告诉编译器这个变量只能被初始化，

且不能被直接修改（实际上可以通过堆栈溢出等方式修改）。而这个变量的值，可以在运行时也可以在编译时指定。

*/
namespace c20 {
	//https://en.cppreference.com/w/cpp/container/array/to_array  
	// Possible implementation
	//c++ 20标准的参考实现方案
	template <class T, std::size_t N, std::size_t... I>
	constexpr std::array<std::remove_cv_t<T>, N>
		to_array_impl(T(&a)[N], std::index_sequence<I...>)
	{
		return { {a[I]...} };
	}


	template <class T, std::size_t N>
	constexpr std::array<std::remove_cv_t<T>, N> to_array(T(&a)[N])
	{
		return to_array_impl(a, std::make_index_sequence<N>{});
	}
	template <class T, std::size_t N, std::size_t... I>
	constexpr std::array<std::remove_cv_t<T>, N>
		to_array_impl(T(&& a)[N], std::index_sequence<I...>)
	{
		return { {std::move(a[I])...} };
	}

	template <class T, std::size_t N>
	constexpr std::array<std::remove_cv_t<T>, N> to_array(T(&& a)[N])
	{
		return to_array_impl(std::move(a), std::make_index_sequence<N>{});
	}

	void c20Demo()
	{
		//auto g_a = to_array<char>({ 256, -1 });  // 数字超出uint8_t范围,编译器还是一样的会让你编译通过和运行，g_a中的两个元素的值将分别为0和255。整型溢出与回绕。
		auto b1 = to_array<uint32_t>({ 0, 1, 3 }); //允许定义uint32_t
		auto b2 = to_array<char>({ 0, 1, 3 });//允许定义char

		// copies a string literal
		auto a1 = to_array("foo");
		static_assert(a1.size() == 4);

		// deduces both element type and length
		auto a2 = to_array({ 0, 2, 1, 3 });
		static_assert(std::is_same_v<decltype(a2), std::array<int, 4>>);

		// deduces length with element type specified
		// implicit conversion happens
		auto a3 = to_array<long>({ 0, 1, 3 });
		static_assert(std::is_same_v<decltype(a3), std::array<long, 3>>);

		auto a4 = to_array<std::pair<int, float>>(
			{ { 3, .0f }, { 4, .1f }, { 4, .1e23f } });
		static_assert(a4.size() == 3);

		// creates a non-copyable std::array
		auto a5 = to_array({ std::make_unique<int>(3) });
		static_assert(a5.size() == 1);

		// error: copying multidimensional arrays is not supported
		// char s[2][6] = { "nice", "thing" };
		// auto a6 = std::to_array(s);
	}
}

namespace constDemo {
	/*
constexpr表达式是指值不会改变并且在编译过程就能得到计算结果的表达式。
声明为constexpr的变量一定是一个const变量，而且必须用常量表达式初始化：

constexpr可以用来修饰变量、函数、构造函数。一旦以上任何元素被constexpr修饰，
那么等于说是告诉编译器 “请大胆地将我看成编译时就能得出常量值的表达式去优化我”。

全局变量和局部变量的存储区域不同，全局变量存放在静态数据区，局部变量存放在栈区。
但还有一个小点就是存放在静态数据区的变量是由低地址向高地址存放的，
但存放在栈区的变量却是由高地址向低地址存放的，

存放在静态数据区的还有静态局部变量和静态全局变量。

constexpr与const的本质区别

　　const主要用于表达“对接口的写权限控制”，即“对于被const修饰的量名(例如const指针变量)，不得通过它对所指对象作任何修改”。
  (但是可以通过其他接口修改该对象)。另外，把对象声明为const也为编译器提供了潜在的优化可能。
  具体来说就是，如果把一个量声明为const，并且没有其他地方对该量作取址运算，
  那么编译器通常(取决于编译期实现)会用该量的实际常量值直接替换掉代码中所有引用该量的地方，
  而不用在最终编译结果中生成对该量的存取指令。

  constexpr的主要功能则是让更多的运算可以在编译期完成，并能保证表达式在语义上是类型安全的。
  (注：相比之下，C语言中#define只能提供简单的文本替换，而不具任何类型检查能力)。
  与const相比，被constexpr修饰的对象则强制要求其初始化表达式能够在编译期完成计算。
  之后所有引用该常量对象的地方，若非必要，一律用计算出来的常量值替换。
*/
	constexpr int func() {
		return 10;
	}
	const int func1() {
		return 10;
	}
	constexpr int func2(const int n) {
		return 10 + n;
	}
	void demo() {
		constexpr int mf = 20;  //20是常量表达式
		constexpr int limit = mf + 1; // mf + 1是常量表达式
		constexpr int sz = func(); //之后当func是一个constexpr函数时才是一条正确的声明语句

		const int* p = nullptr;        //p是一个指向整形常量的指针
		constexpr int* q = nullptr;   //q是一个指向整数的常量指针

		int arr[func()];
		//int arr1[func1()];  //无法编译通过

		int i = 10;
		static int j = 100;
		static int k = 100;


		cout << "j=" << j << std::endl;

		// constexpr int* p1 = &i; //constexpr指针所指变量必须是全局变量或者static变量(即存储在静态数据区的变量)。
		constexpr int* p2 = &j;
		*p2 = 8;

		cout << "j=" << j << std::endl << std::endl;

		static constexpr int N = 3;
		/*
	   在这里const和constexpr在修饰不同的东西，constexpr和const都必须要有。
	   constexpr表示NP指针本身是常量表达式，而const表示指向的值是一个常量。
	   去掉const之后无法编译，因为不能用正常指针指向常量。
		*/
		constexpr const int* NP = &N;

		//p2 = &k; //error

	}

}
namespace c17Array {



	template<typename R, typename P, size_t N, size_t... I>
	constexpr array<R, N> to_array_impl(P(&a)[N], std::index_sequence<I...>) noexcept
	{
		return { {a[I]...} };
	}

	template<typename T, size_t N>
	constexpr auto to_array(T(&a)[N]) noexcept
	{
		return to_array_impl<std::remove_cv_t<T>, T, N>(a, std::make_index_sequence<N>{});
	}

	template<typename R, typename P, size_t N, size_t... I>
	constexpr array<R, N> to_array_impl(P(&& a)[N], std::index_sequence<I...>) noexcept
	{
		return { {move(a[I])...} };
	}

	template<typename T, size_t N>
	constexpr auto to_array(T(&& a)[N]) noexcept
	{
		return to_array_impl<std::remove_cv_t<T>, T, N>(move(a), std::make_index_sequence<N>{});
	}



	//最终效果
	template<typename R, typename P, size_t N>
	constexpr auto to_typed_array(P(&a)[N]) noexcept
	{
		return to_array_impl<R, P, N>(a, std::make_index_sequence<N>{});
	}

	template<typename R, typename P, size_t N>
	constexpr auto to_typed_array(P(&& a)[N]) noexcept
	{
		return to_array_impl<R, P, N>(move(a), std::make_index_sequence<N>{});
	}

	template<typename T, size_t N>
	constexpr std::array<T, N> Sort(const std::array<T, N>& numbers) noexcept
	{
		std::array<T, N> sorted(numbers);
		for (int i = 0; i < N; ++i) {
			for (int j = N - 1; j > i; --j) {
				if (sorted[j] < sorted[j - 1]) {
					T t = sorted[j];
					sorted[j] = sorted[j - 1];
					sorted[j - 1] = t;
				}
			}
		}
		return sorted;
	}



	int main()
	{

		constDemo::demo();
		constexpr std::array ary1{ 4, 2, 3, 1 };
		//   constexpr std::array<char> ary2{ 4, 2, 3, 1 };

		constexpr std::array<int, 4> before{ 4, 2, 3, 1 };
		constexpr std::array<int, 4> after = Sort(before);
		static_assert(after[0] == 1);
		static_assert(after[1] == 2);
		static_assert(after[2] == 3);
		static_assert(after[3] == 4);


		{
			// copies a string literal
			auto a1 = to_array("foo");
			static_assert(a1.size() == 4);

			// deduces both element type and length
			auto a2 = to_array({ 0, 2, 1, 3 });
			static_assert(std::is_same_v<decltype(a2), std::array<int, 4>>);

			// deduces length with element type specified
			// implicit conversion happens
			auto a3 = to_array<long>({ 0, 1, 3 });
			static_assert(std::is_same_v<decltype(a3), std::array<long, 3>>);

			auto a4 = to_array<std::pair<int, float>>(
				{ { 3, .0f }, { 4, .1f }, { 4, .1e23f } });
			static_assert(a4.size() == 3);

			// creates a non-copyable std::array
			auto a5 = to_array({ std::make_unique<int>(3) });
			static_assert(a5.size() == 1);

			// error: copying multidimensional arrays is not supported
			// char s[2][6] = { "nice", "thing" };
			// auto a6 = std::to_array(s);
		}

		array g_cfgPara1 = { 1, 2, 5, 6, 7, 9, 3, 4 };  // 数组大小与成员类型自动推导
		for (size_t i = 0; i < g_cfgPara1.size(); i++)
			cout << g_cfgPara1[i] << endl;



		auto g_cfgPara21 = to_array<int>({ 1, 2, 5, 6, });  // 类型不是uint32_t？
		auto g_cfgPara22 = to_array<uint32_t>({ 1, 2, 5, 10 });
		for (size_t i = 0; i < g_cfgPara22.size(); i++)
			cout << g_cfgPara22[i] << endl;
		auto g_cfgPara23 = to_array<long>({ 1, 2, 5, 10 });
		auto g_a = to_array<uint8_t>({ 1, 2 });
		for (size_t i = 0; i < g_a.size(); i++)
			cout << g_a[i] << endl;
		/*
		auto g_a = to_typed_array<uint8_t>({ 256, -1 });  // 数字超出uint8_t范围,编译器还是一样的会让你编译通过和运行，g_a中的两个元素的值将分别为0和255。整型溢出与回绕。
		for (size_t i = 0; i < g_a.size(); i++)
			cout << g_a[i] << endl;
	*/

	// auto g_cfgPara3 = to_typed_array<uint32_t>({ 1, 2, 5, 6, 7, 9, 3, 4 });  // 自动把元素转换成uint32_t



		return 0;
	}
}
